/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { expect, suite, test } from 'vitest';

import ts from '~/vendor/ohos-typescript';

import { and, is, NodePredicate, not, or } from '~/type/Predicate';
import { AsExpression, BinaryExpression, ConditionalExpression } from '~/type/ast/Expression';

suite('is/as test', () => {
    test('built-in type', () => {
        const goodNode = { kind: ts.SyntaxKind.BinaryExpression } as ts.Node;
        const badNode = { kind: ts.SyntaxKind.ConditionalExpression } as ts.Node;

        expect(is(goodNode, BinaryExpression)).toBeTruthy();
        expect(is(badNode, BinaryExpression)).toBeFalsy();
    });

    type ConditionalOrBinaryNode = ts.ConditionalExpression | ts.BinaryExpression;
    const ConditionalOrBinary: NodePredicate<ConditionalOrBinaryNode> = {
        is(node: ts.Node): node is ConditionalOrBinaryNode {
            return ts.isConditionalExpression(node) || ts.isBinaryExpression(node);
        },
    };

    const conditionNode = { kind: ts.SyntaxKind.ConditionalExpression } as ts.Node;
    const binaryNode = { kind: ts.SyntaxKind.BinaryExpression } as ts.Node;
    const asNode = { kind: ts.SyntaxKind.AsExpression } as ts.Node;
    const otherNode = { kind: ts.SyntaxKind.ExpressionStatement } as ts.Node;

    test('custom type', () => {
        expect(is(conditionNode, ConditionalOrBinary)).toBeTruthy();
        expect(is(binaryNode, ConditionalOrBinary)).toBeTruthy();
        expect(is(otherNode, ConditionalOrBinary)).toBeFalsy();
    });

    test('combine type', () => {
        expect(or(ConditionalExpression, BinaryExpression).is(conditionNode)).toBeTruthy();
        expect(or(ConditionalExpression, BinaryExpression).is(binaryNode)).toBeTruthy();
        expect(or(ConditionalExpression, BinaryExpression).is(otherNode)).toBeFalsy();

        expect(and(ConditionalOrBinary, BinaryExpression).is(binaryNode)).toBeTruthy();
        expect(and(ConditionalOrBinary, BinaryExpression).is(conditionNode)).toBeFalsy();
        expect(not(BinaryExpression).is(binaryNode)).toBeFalsy();
        expect(not(BinaryExpression).is(conditionNode)).toBeTruthy();
    });

    test('nest combine type', () => {
        const Nested = or(AsExpression, or(ConditionalExpression, BinaryExpression));

        expect(Nested.is(conditionNode)).toBeTruthy();
        expect(Nested.is(binaryNode)).toBeTruthy();
        expect(Nested.is(asNode)).toBeTruthy();
        expect(Nested.is(otherNode)).toBeFalsy();
    });
});
